package state

import (
	"hundsun.com/hsl/hschain/contract/evm/ext/common"
	pbcom "hundsun.com/hsl/hschain/protos/common"
)

// DataChange evm执行时候数据变更接口
type DataChange interface {
	revert(sdb *EVMStateDB)
}

// Snapshot 快照结构，快照版本及当前版本的数据变更对象
type Snapshot struct {
	id      int
	entries []DataChange
	sdb     *EVMStateDB
}

// GetID 获取ID
func (ver *Snapshot) GetID() int {
	return ver.id
}

// 回滚当前版本
func (ver *Snapshot) revert() bool {
	if ver.entries == nil {
		return true
	}
	for _, entry := range ver.entries {
		entry.revert(ver.sdb)
	}
	return true
}

// 添加变更
func (ver *Snapshot) append(entry DataChange) {
	ver.entries = append(ver.entries, entry)
}

type (
	// 创建合约对象变更事件
	createAccountChange struct {
		account common.Address
	}

	// Account中内容变更
	balanceChange struct {
		account common.Address
		prev    uint64
	}

	// EVMContractInfo中内容变更
	codeChange struct {
		account  common.Address
		prevcode []byte
		prevhash common.Hash
		preType  pbcom.Account_AccountType
	}
	nonceChange struct {
		account common.Address
		prev    uint64
	}
	suicideChange struct {
		account common.Address
		prev    bool
	}
	abiChange struct {
		account common.Address
		prevabi string
	}

	// 存储状态变更事件
	storageChange struct {
		account       common.Address
		key, prevalue common.Hash
	}

	// 合约生成日志事件
	addLogChange struct {
		txhash common.Hash
	}

	// 合约生成sha3事件
	addPreimageChange struct {
		hash common.Hash
	}
)

func (ch createAccountChange) revert(s *EVMStateDB) {
	delete(s.stateObjects, ch.account)
}

func (ch balanceChange) revert(sdb *EVMStateDB) {
	so := sdb.stateObjects[ch.account]
	if so != nil {
		so.acc.Balance = ch.prev
	}
}

func (ch codeChange) revert(sdb *EVMStateDB) {
	so := sdb.stateObjects[ch.account]
	if so != nil {
		so.info.Code = ch.prevcode
		so.codeHash = ch.prevhash
		so.acc.Type = ch.preType
	}
}

func (ch nonceChange) revert(sdb *EVMStateDB) {
	so := sdb.stateObjects[ch.account]
	if so != nil {
		so.info.Nonce = ch.prev
	}
}

func (ch suicideChange) revert(sdb *EVMStateDB) {
	// 如果已经自杀过了，不处理
	if ch.prev {
		return
	}
	so := sdb.stateObjects[ch.account]
	if so != nil {
		so.info.Suicided = ch.prev
	}
}

func (ch abiChange) revert(sdb *EVMStateDB) {
	so := sdb.stateObjects[ch.account]
	if so != nil {
		so.info.Abi = ch.prevabi
	}
}

func (ch storageChange) revert(sdb *EVMStateDB) {
	so := sdb.stateObjects[ch.account]
	if so != nil {
		so.SetState(ch.key, ch.prevalue)
	}
}

func (ch addLogChange) revert(sdb *EVMStateDB) {
	if len(sdb.logs) > 0 { //每次回退只删除最后一个log
		sdb.logs = sdb.logs[:len(sdb.logs)-1]
	}
}

func (ch addPreimageChange) revert(sdb *EVMStateDB) {
	delete(sdb.preimages, ch.hash)
}
